// Copyright 2013 The Flutter Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:typed_data';

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart';

import 'webview_controller_test.mocks.dart';

@GenerateMocks(<Type>[PlatformWebViewController, PlatformNavigationDelegate])
void main() {
  test('loadFile', () async {
    final mockPlatformWebViewController = MockPlatformWebViewController();

    final webViewController = WebViewController.fromPlatform(
      mockPlatformWebViewController,
    );

    await webViewController.loadFile('file/path');
    verify(mockPlatformWebViewController.loadFile('file/path'));
  });

  test('loadFlutterAsset', () async {
    final mockPlatformWebViewController = MockPlatformWebViewController();

    final webViewController = WebViewController.fromPlatform(
      mockPlatformWebViewController,
    );

    await webViewController.loadFlutterAsset('file/path');
    verify(mockPlatformWebViewController.loadFlutterAsset('file/path'));
  });

  test('loadHtmlString', () async {
    final mockPlatformWebViewController = MockPlatformWebViewController();

    final webViewController = WebViewController.fromPlatform(
      mockPlatformWebViewController,
    );

    await webViewController.loadHtmlString('html', baseUrl: 'baseUrl');
    verify(
      mockPlatformWebViewController.loadHtmlString('html', baseUrl: 'baseUrl'),
    );
  });

  test('loadRequest', () async {
    final mockPlatformWebViewController = MockPlatformWebViewController();

    final webViewController = WebViewController.fromPlatform(
      mockPlatformWebViewController,
    );

    await webViewController.loadRequest(
      Uri(scheme: 'https', host: 'dart.dev'),
      method: LoadRequestMethod.post,
      headers: <String, String>{'a': 'header'},
      body: Uint8List(0),
    );

    final params =
        verify(
              mockPlatformWebViewController.loadRequest(captureAny),
            ).captured[0]
            as LoadRequestParams;
    expect(params.uri, Uri(scheme: 'https', host: 'dart.dev'));
    expect(params.method, LoadRequestMethod.post);
    expect(params.headers, <String, String>{'a': 'header'});
    expect(params.body, Uint8List(0));
  });

  test('currentUrl', () async {
    final mockPlatformWebViewController = MockPlatformWebViewController();
    when(
      mockPlatformWebViewController.currentUrl(),
    ).thenAnswer((_) => Future<String>.value('https://dart.dev'));

    final webViewController = WebViewController.fromPlatform(
      mockPlatformWebViewController,
    );

    await expectLater(
      webViewController.currentUrl(),
      completion('https://dart.dev'),
    );
  });

  test('canGoBack', () async {
    final mockPlatformWebViewController = MockPlatformWebViewController();
    when(
      mockPlatformWebViewController.canGoBack(),
    ).thenAnswer((_) => Future<bool>.value(false));

    final webViewController = WebViewController.fromPlatform(
      mockPlatformWebViewController,
    );

    await expectLater(webViewController.canGoBack(), completion(false));
  });

  test('canGoForward', () async {
    final mockPlatformWebViewController = MockPlatformWebViewController();
    when(
      mockPlatformWebViewController.canGoForward(),
    ).thenAnswer((_) => Future<bool>.value(true));

    final webViewController = WebViewController.fromPlatform(
      mockPlatformWebViewController,
    );

    await expectLater(webViewController.canGoForward(), completion(true));
  });

  test('goBack', () async {
    final mockPlatformWebViewController = MockPlatformWebViewController();

    final webViewController = WebViewController.fromPlatform(
      mockPlatformWebViewController,
    );

    await webViewController.goBack();
    verify(mockPlatformWebViewController.goBack());
  });

  test('goForward', () async {
    final mockPlatformWebViewController = MockPlatformWebViewController();

    final webViewController = WebViewController.fromPlatform(
      mockPlatformWebViewController,
    );

    await webViewController.goForward();
    verify(mockPlatformWebViewController.goForward());
  });

  test('reload', () async {
    final mockPlatformWebViewController = MockPlatformWebViewController();

    final webViewController = WebViewController.fromPlatform(
      mockPlatformWebViewController,
    );

    await webViewController.reload();
    verify(mockPlatformWebViewController.reload());
  });

  test('clearCache', () async {
    final mockPlatformWebViewController = MockPlatformWebViewController();

    final webViewController = WebViewController.fromPlatform(
      mockPlatformWebViewController,
    );

    await webViewController.clearCache();
    verify(mockPlatformWebViewController.clearCache());
  });

  test('clearLocalStorage', () async {
    final mockPlatformWebViewController = MockPlatformWebViewController();

    final webViewController = WebViewController.fromPlatform(
      mockPlatformWebViewController,
    );

    await webViewController.clearLocalStorage();
    verify(mockPlatformWebViewController.clearLocalStorage());
  });

  test('runJavaScript', () async {
    final mockPlatformWebViewController = MockPlatformWebViewController();

    final webViewController = WebViewController.fromPlatform(
      mockPlatformWebViewController,
    );

    await webViewController.runJavaScript('1 + 1');
    verify(mockPlatformWebViewController.runJavaScript('1 + 1'));
  });

  test('runJavaScriptReturningResult', () async {
    final mockPlatformWebViewController = MockPlatformWebViewController();
    when(
      mockPlatformWebViewController.runJavaScriptReturningResult('1 + 1'),
    ).thenAnswer((_) => Future<String>.value('2'));

    final webViewController = WebViewController.fromPlatform(
      mockPlatformWebViewController,
    );

    await expectLater(
      webViewController.runJavaScriptReturningResult('1 + 1'),
      completion('2'),
    );
  });

  test('addJavaScriptChannel', () async {
    final mockPlatformWebViewController = MockPlatformWebViewController();

    final webViewController = WebViewController.fromPlatform(
      mockPlatformWebViewController,
    );

    void onMessageReceived(JavaScriptMessage message) {}
    await webViewController.addJavaScriptChannel(
      'name',
      onMessageReceived: onMessageReceived,
    );

    final params =
        verify(
              mockPlatformWebViewController.addJavaScriptChannel(captureAny),
            ).captured[0]
            as JavaScriptChannelParams;
    expect(params.name, 'name');
    expect(params.onMessageReceived, onMessageReceived);
  });

  test('removeJavaScriptChannel', () async {
    final mockPlatformWebViewController = MockPlatformWebViewController();

    final webViewController = WebViewController.fromPlatform(
      mockPlatformWebViewController,
    );

    await webViewController.removeJavaScriptChannel('channel');
    verify(mockPlatformWebViewController.removeJavaScriptChannel('channel'));
  });

  test('getTitle', () async {
    final mockPlatformWebViewController = MockPlatformWebViewController();
    when(
      mockPlatformWebViewController.getTitle(),
    ).thenAnswer((_) => Future<String>.value('myTitle'));

    final webViewController = WebViewController.fromPlatform(
      mockPlatformWebViewController,
    );

    await expectLater(webViewController.getTitle(), completion('myTitle'));
  });

  test('scrollTo', () async {
    final mockPlatformWebViewController = MockPlatformWebViewController();

    final webViewController = WebViewController.fromPlatform(
      mockPlatformWebViewController,
    );

    await webViewController.scrollTo(2, 3);
    verify(mockPlatformWebViewController.scrollTo(2, 3));
  });

  test('scrollBy', () async {
    final mockPlatformWebViewController = MockPlatformWebViewController();

    final webViewController = WebViewController.fromPlatform(
      mockPlatformWebViewController,
    );

    await webViewController.scrollBy(2, 3);
    verify(mockPlatformWebViewController.scrollBy(2, 3));
  });

  test('setVerticalScrollBarEnabled', () async {
    final mockPlatformWebViewController = MockPlatformWebViewController();

    final webViewController = WebViewController.fromPlatform(
      mockPlatformWebViewController,
    );

    await webViewController.setVerticalScrollBarEnabled(true);
    verify(mockPlatformWebViewController.setVerticalScrollBarEnabled(true));
  });

  test('setHorizontalScrollBarEnabled', () async {
    final mockPlatformWebViewController = MockPlatformWebViewController();

    final webViewController = WebViewController.fromPlatform(
      mockPlatformWebViewController,
    );

    await webViewController.setHorizontalScrollBarEnabled(false);
    verify(mockPlatformWebViewController.setHorizontalScrollBarEnabled(false));
  });

  test('supportsSetScrollBarsEnabled', () async {
    final mockPlatformWebViewController = MockPlatformWebViewController();
    when(
      mockPlatformWebViewController.supportsSetScrollBarsEnabled(),
    ).thenReturn(true);

    final webViewController = WebViewController.fromPlatform(
      mockPlatformWebViewController,
    );

    expect(await webViewController.supportsSetScrollBarsEnabled(), true);
    verify(mockPlatformWebViewController.supportsSetScrollBarsEnabled());
  });

  test('getScrollPosition', () async {
    final mockPlatformWebViewController = MockPlatformWebViewController();
    when(
      mockPlatformWebViewController.getScrollPosition(),
    ).thenAnswer((_) => Future<Offset>.value(const Offset(2, 3)));

    final webViewController = WebViewController.fromPlatform(
      mockPlatformWebViewController,
    );

    await expectLater(
      webViewController.getScrollPosition(),
      completion(const Offset(2.0, 3.0)),
    );
  });

  test('enableZoom', () async {
    final mockPlatformWebViewController = MockPlatformWebViewController();

    final webViewController = WebViewController.fromPlatform(
      mockPlatformWebViewController,
    );

    await webViewController.enableZoom(false);
    verify(mockPlatformWebViewController.enableZoom(false));
  });

  test('setBackgroundColor', () async {
    final mockPlatformWebViewController = MockPlatformWebViewController();

    final webViewController = WebViewController.fromPlatform(
      mockPlatformWebViewController,
    );

    await webViewController.setBackgroundColor(Colors.green);
    verify(mockPlatformWebViewController.setBackgroundColor(Colors.green));
  });

  test('setJavaScriptMode', () async {
    final mockPlatformWebViewController = MockPlatformWebViewController();

    final webViewController = WebViewController.fromPlatform(
      mockPlatformWebViewController,
    );

    await webViewController.setJavaScriptMode(JavaScriptMode.disabled);
    verify(
      mockPlatformWebViewController.setJavaScriptMode(JavaScriptMode.disabled),
    );
  });

  test('setUserAgent', () async {
    final mockPlatformWebViewController = MockPlatformWebViewController();

    final webViewController = WebViewController.fromPlatform(
      mockPlatformWebViewController,
    );

    await webViewController.setUserAgent('userAgent');
    verify(mockPlatformWebViewController.setUserAgent('userAgent'));
  });

  test('setNavigationDelegate', () async {
    final mockPlatformWebViewController = MockPlatformWebViewController();
    final webViewController = WebViewController.fromPlatform(
      mockPlatformWebViewController,
    );

    final mockPlatformNavigationDelegate = MockPlatformNavigationDelegate();
    final navigationDelegate = NavigationDelegate.fromPlatform(
      mockPlatformNavigationDelegate,
    );

    await webViewController.setNavigationDelegate(navigationDelegate);
    verify(
      mockPlatformWebViewController.setPlatformNavigationDelegate(
        mockPlatformNavigationDelegate,
      ),
    );
  });

  test('onPermissionRequest', () async {
    var permissionRequestCallbackCalled = false;

    final mockPlatformWebViewController = MockPlatformWebViewController();
    WebViewController.fromPlatform(
      mockPlatformWebViewController,
      onPermissionRequest: (WebViewPermissionRequest request) {
        permissionRequestCallbackCalled = true;
      },
    );

    final requestCallback =
        verify(
              mockPlatformWebViewController.setOnPlatformPermissionRequest(
                captureAny,
              ),
            ).captured.single
            as void Function(PlatformWebViewPermissionRequest request);

    requestCallback(const TestPlatformWebViewPermissionRequest());
    expect(permissionRequestCallbackCalled, isTrue);
  });

  test('setConsoleLogCallback', () async {
    final mockPlatformWebViewController = MockPlatformWebViewController();

    final webViewController = WebViewController.fromPlatform(
      mockPlatformWebViewController,
    );

    void onConsoleMessage(JavaScriptConsoleMessage message) {}

    await webViewController.setOnConsoleMessage(onConsoleMessage);

    verify(mockPlatformWebViewController.setOnConsoleMessage(onConsoleMessage));
  });

  test('setOnJavaScriptAlertDialog', () async {
    final mockPlatformWebViewController = MockPlatformWebViewController();

    final webViewController = WebViewController.fromPlatform(
      mockPlatformWebViewController,
    );

    Future<void> onJavaScriptAlertDialog(
      JavaScriptAlertDialogRequest request,
    ) async {
      return;
    }

    await webViewController.setOnJavaScriptAlertDialog(onJavaScriptAlertDialog);
    verify(
      mockPlatformWebViewController.setOnJavaScriptAlertDialog(
        onJavaScriptAlertDialog,
      ),
    );
  });

  test('setOnJavaScriptConfirmDialog', () async {
    final mockPlatformWebViewController = MockPlatformWebViewController();

    final webViewController = WebViewController.fromPlatform(
      mockPlatformWebViewController,
    );

    Future<bool> onJavaScriptConfirmDialog(
      JavaScriptConfirmDialogRequest request,
    ) async {
      return true;
    }

    await webViewController.setOnJavaScriptConfirmDialog(
      onJavaScriptConfirmDialog,
    );
    verify(
      mockPlatformWebViewController.setOnJavaScriptConfirmDialog(
        onJavaScriptConfirmDialog,
      ),
    );
  });

  test('setOnJavaScriptTextInputDialog', () async {
    final mockPlatformWebViewController = MockPlatformWebViewController();

    final webViewController = WebViewController.fromPlatform(
      mockPlatformWebViewController,
    );

    Future<String> onJavaScriptTextInputDialog(
      JavaScriptTextInputDialogRequest request,
    ) async {
      return 'text';
    }

    await webViewController.setOnJavaScriptTextInputDialog(
      onJavaScriptTextInputDialog,
    );
    verify(
      mockPlatformWebViewController.setOnJavaScriptTextInputDialog(
        onJavaScriptTextInputDialog,
      ),
    );
  });

  test('getUserAgent', () async {
    final mockPlatformWebViewController = MockPlatformWebViewController();

    const userAgent = 'str';

    when(
      mockPlatformWebViewController.getUserAgent(),
    ).thenAnswer((_) => Future<String?>.value(userAgent));

    final webViewController = WebViewController.fromPlatform(
      mockPlatformWebViewController,
    );
    await expectLater(webViewController.getUserAgent(), completion(userAgent));
  });

  test('setOnScrollPositionChange', () async {
    final mockPlatformWebViewController = MockPlatformWebViewController();

    final webViewController = WebViewController.fromPlatform(
      mockPlatformWebViewController,
    );

    void onScrollPositionChange(ScrollPositionChange change) {}

    await webViewController.setOnScrollPositionChange(onScrollPositionChange);

    verify(
      mockPlatformWebViewController.setOnScrollPositionChange(
        onScrollPositionChange,
      ),
    );
  });

  test('setOverScrollMode', () async {
    final mockPlatformWebViewController = MockPlatformWebViewController();

    final webViewController = WebViewController.fromPlatform(
      mockPlatformWebViewController,
    );

    await webViewController.setOverScrollMode(WebViewOverScrollMode.never);
    verify(
      mockPlatformWebViewController.setOverScrollMode(
        WebViewOverScrollMode.never,
      ),
    );
  });
}

class TestPlatformWebViewPermissionRequest
    extends PlatformWebViewPermissionRequest {
  const TestPlatformWebViewPermissionRequest()
    : super(types: const <WebViewPermissionResourceType>{});

  @override
  Future<void> grant() async {}

  @override
  Future<void> deny() async {}
}
